/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.common.util;

import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import io.iec.edp.caf.common.environment.EnvironmentUtil;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.Socket;
import java.util.*;
import java.util.stream.Collectors;

public class CommandUtil {
    public static void newProcess(String directory, String command) {
        ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", command);
        builder.directory(new File(directory));
        builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        builder.redirectError(ProcessBuilder.Redirect.INHERIT);
        builder.redirectInput(ProcessBuilder.Redirect.INHERIT);
        builder.redirectErrorStream(true);

        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;

        Process p;
        try {
            p = builder.start();
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            String line;
            while (true) {
                line = br.readLine();
                if (line == null) {
                    break;
                }
                System.out.println(line);
            }

            p.waitFor();
            int exit = p.exitValue();
            if (exit != 0) {
                throw new RuntimeException("程序调用异常");
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException("程序调用异常", e);
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
                if (ir != null) {
                    ir.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void start(String serverPath) {
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec("cmd /k start " + serverPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//    public static int getPid(Process process) {
//        int pid = 0;
//        try {
//            Field field = process.getClass().getDeclaredField("handle");
//            field.setAccessible(true);
//            Long pidL = Kernel32.INSTANCE.GetProcessId((Long) field.get(process));
//            pid = pidL.intValue();
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//        return pid;
//    }

    // 通过获取当前运行主机的pidName，截取获得他的pid
    public static int getCurrentPid() {
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        String pidName = runtime.getName();// 5296@dell-PC
        int pid = Integer.parseInt(pidName.substring(0, pidName.indexOf("@")));
        return pid;
    }

    // 通过Pid获取PidName
    public static String getPidNameByPid(int pid) throws Exception {
        String pidName = null;
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV /FI \"PID EQ " + pid + "\"");
            is = p.getInputStream(); // "javaw.exe","3856","Console","1","72,292
            // K"从这个进程中获取对应的PidName
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                if (line.indexOf(String.valueOf(pid)) != -1) {
                    array = line.split(",");
                    line = array[0].replaceAll("\"", "");
                    line = line.replaceAll(".exe", "");// 考虑pidName后缀为exe或者EXE
                    line = line.replaceAll(".exe".toUpperCase(), "");
                    pidName = line;
                }
            }
        } catch (IOException localIOException) {
            throw new Exception("获取进程名称出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return pidName;
    }

    // 根据Pid获取当前进程的CPU
    public static String getCPUByPID(int pid) throws Exception {
        if (pid == 0) {
            return null;
        }
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV /FI \"PID EQ " + pid + "\"");
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                if (!"".equals(line)) {
                    array = line.split("\",\"");
                    line = array[3].replaceAll("\"", "");
                    return line;
                }
            }
        } catch (Exception localException) {
            throw new Exception("获取进程CPU信息出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        if (br != null) {
            br.close();
        }
        if (ir != null) {
            ir.close();
        }
        if (is != null) {
            is.close();
        }
        return null;
    }

    // 根据Pid获取当前进程的memory
    public static String getMemByPID(int pid) throws Exception {
        if (pid == 0) {
            return null;
        }
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV /FI \"PID EQ " + pid + "\"");
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                if (!"".equals(line)) {
                    array = line.split("\",\"");
                    if (array.length < 5) {
                        continue;
                    }
                    line = array[4].replaceAll("\"", "");
                    return line;
                }
            }
        } catch (IOException localIOException) {
            throw new Exception("获取进程内存信息出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        if (br != null) {
            br.close();
        }
        if (ir != null) {
            ir.close();
        }
        if (is != null) {
            is.close();
        }
        return null;
    }

    // 根据Pid将进程干掉
    public static void killProcessByPid(int pid) throws Exception {
        Runtime.getRuntime().exec("taskkill /F /PID " + pid);
    }

    // 根据PidName将进程干掉
    public static void killProcessByPidName(String pidName) throws Exception {
        Runtime.getRuntime().exec("taskkill /F /IM " + pidName);
    }

    // 根据PidName获取当前的Pid的list集合
    public static List<Integer> getPidsByPidName(String pidName) throws Exception {
        List<Integer> pidList = new ArrayList<>();
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            String imageName = pidName;
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV /FI \"IMAGENAME EQ " + imageName + "\"");
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                if (line.indexOf(imageName) != -1) {
                    array = line.split(",");
                    line = array[1].replaceAll("\"", "");
                    pidList.add(Integer.parseInt(line));
                }
            }
        } catch (IOException localIOException) {
            throw new Exception("获取进程ID出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return pidList;
    }

    // 获取所有的pid
    public static List<Integer> getPidsByFilter(String filter) throws Exception {
        List<Integer> pids = new ArrayList<>();
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV | findstr " + filter);
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                array = line.split(",");
                int pid = Integer.parseInt(array[1].replace("\"", ""));
                if (pid != 0) {
                    pids.add(pid);
                }
            }
        } catch (IOException localIOException) {
            throw new Exception("获取系统所有进程名出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return pids;
    }

    // 获取当前系统的所有的PidName
    public static Set<String> getCurrOsAllPidNameSet() throws Exception {
        Set<String> pidNameSet = new HashSet<>();
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c TASKLIST /NH /FO CSV");
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                array = line.split(",");
                line = array[0].replaceAll("\"", "");
                line = line.replaceAll(".exe", "");
                line = line.replaceAll(".exe".toUpperCase(), "");
                if (line != "") {
                    pidNameSet.add(line);
                }
            }
        } catch (IOException localIOException) {
            throw new Exception("获取系统所有进程名出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return pidNameSet;
    }

    // 判断当前pid是否退出，判断根据pid查询的内存是否为空来决定
    public static boolean isPidExist(int pid) throws Exception {
        return getMemByPID(pid) != null;
    }

    // 对启动路径和运行参数的拼接
    public static String getCommandFormatStr(String proPath) {// 这样的思路就可以控制俩种参数的输入
        return getCommandFormatStr(proPath, null);
    }

    public static String getCommandFormatStr(String proPath, String runArgs) {
        StringBuffer command = new StringBuffer();
        command.append("\"");
        command.append(proPath);
        command.append("\"");
        if (runArgs != "") {
            command.append(" ").append(runArgs);
        }
        return command.toString();
    }

    // 执行完相应的命令行就退出cmd
    private static String getCommandByCmd(String cmdStr) {
        StringBuffer command = new StringBuffer();
        command.append("cmd /C ");
        command.append(cmdStr);
        return command.toString();
    }

    // 通过cmd打开对应的文件
    public static void openDir(String fileDir) throws Exception {// 打开cmd，执行explorer
        Runtime.getRuntime().exec("cmd /c start explorer " + fileDir);// explorer.exe是Windows的程序管理器或者文件资源管理器
    }

    // 根据当前的Pid获取当前进程的端口
    public static int getPortByPID(int pid) {
        if (pid == 0) {
            return 0;
        }
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line = "";
        String LISTENING_STATE_TYPE = "LISTENING";// 状态值
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("cmd /c netstat /ano | findstr [ | findstr " + pid + " | findstr " + LISTENING_STATE_TYPE);
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            do {
                if (line.indexOf(String.valueOf(pid)) != -1) {
                    line = line.replaceFirst("\\s+", "");
                    array = line.split("\\s+");
                    int port = Integer.parseInt(array[1].split(":")[3].trim());
                    return port;
                }
                if ((line = br.readLine()) == null) {
                    break;
                }
            } while (pid != 0);
        } catch (IOException localIOException) {
            throw new RuntimeException("获取进程端口信息出错！");
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
                if (ir != null) {
                    ir.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return 0;
    }


    // 根据当前的Pid获取当前进程的端口
    public static Map<String, List<String>> getPortsByPID(int pid) throws Exception {
        if (pid == 0) {
            return null;
        }
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line = null;
        String TCP_TYPE = "TCP";
        String UDP_TYPE = "UDP";
        String LISTENING_STATE_TYPE = "LISTENING";// 状态值
        Map<String, List<String>> portMap = new HashMap<>();
        List<String> tcpPortList = new ArrayList<>();
        List<String> udpPortList = new ArrayList<>();
        portMap.put(TCP_TYPE, tcpPortList);
        portMap.put(UDP_TYPE, udpPortList);
        String[] array;
        try {
            Process p = Runtime.getRuntime().exec("netstat /ano");
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            do {
                if (line.indexOf(pid) != -1) {
                    line = line.replaceFirst("\\s+", "");
                    if (line.indexOf(TCP_TYPE) != -1) {
                        if (line.indexOf(LISTENING_STATE_TYPE) != -1) {
                            array = line.split("\\s+");
                            String port = array[1].split(":")[1];
                            tcpPortList.add(port);
                        }
                    } else {
                        array = line.split("\\s+");
                        String port = array[1].split(":")[1];
                        udpPortList.add(port);
                    }
                }
                if ((line = br.readLine()) == null) {
                    break;
                }
            } while (pid != 0);
        } catch (IOException localIOException) {
            throw new Exception("获取进程端口信息出错！");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return portMap;
    }

    public static List<Integer> getPidsByServerPath(String serverPath) {
        List<Integer> pids = new ArrayList<>();
        ArrayList<String> inputs;
        String cmd = "wmic process where caption=\"java.exe\" get caption,executablepath,processid";
        try {
            inputs = getInputs(cmd);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取进程信息失败：" + cmd, e);
        }
        List<String> processInfos = inputs.stream().filter(input -> input.contains(serverPath)).collect(Collectors.toList());
        if (processInfos != null) {
            processInfos.forEach(processInfo -> {
                String[] splits = processInfo.split("\\s+");
                int pid = Integer.parseInt(splits[2].trim());
                pids.add(pid);
            });
        }
        return pids;
    }

    // 根据当前的Pid获取当前进程的端口
    public static ArrayList<String> getInputs(String cmd) throws Exception {
        ArrayList<String> inputs = new ArrayList<>();
        InputStream is = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        String line;
        try {
            Process p = Runtime.getRuntime().exec(cmd);
            is = p.getInputStream();
            ir = new InputStreamReader(is);
            br = new BufferedReader(ir);
            while ((line = br.readLine()) != null) {
                inputs.add(line);
            }

            p.waitFor();
            int exit = p.exitValue();
            if (exit != 0) {
                throw new RuntimeException("程序调用异常");
            }
        } catch (IOException localIOException) {
            throw new Exception("执行命令：" + cmd + "出错。");
        } finally {
            if (br != null) {
                br.close();
            }
            if (ir != null) {
                ir.close();
            }
            if (is != null) {
                is.close();
            }
        }
        return inputs;
    }

    public static List<Integer> getPidsByPort(int port) {
        ArrayList<Integer> pids = new ArrayList<>();
        try {
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("cmd /c netstat -ano | findstr " + port);

            BufferedReader stdInput = new BufferedReader(new
                    InputStreamReader(proc.getInputStream()));
            String s = null;
            if ((s = stdInput.readLine()) != null) {
                int index = s.lastIndexOf(" ");
                String sc = s.substring(index);
                pids.add(Integer.parseInt(sc.trim()));
                //rt.exec("cmd /c Taskkill /PID" +sc+" /T /F");
            }
            return pids;
        } catch (Exception e) {
            throw new RuntimeException("获取pid失败");
        }
    }

    public static boolean isPortAvailable(int port) {
        try (Socket ignored = new Socket("localhost", port)) {
            return false;
        } catch (IOException ignored) {
            return true;
        }
    }

    public static boolean isWindows() {
        String OS = System.getProperty("os.name").toLowerCase();
        boolean isWindows = OS.startsWith("windows");
        return isWindows;
    }

    // 获取当前环境的Java_Home
    public static String getJavaHome(boolean isDeployTool) {
        String javaHomeInServer = getJavaHomeInServer(isDeployTool);
        if (new File(javaHomeInServer).exists()) {
            return javaHomeInServer;
        }

        String javaHome = System.getenv("JAVA_HOME");
        javaHome = javaHome == null ? System.getProperty("java.home") : javaHome;

        if (Utils.isNullOrEmpty(javaHome)) {
            throw new RuntimeException("请配置系统变量JAVA_HOME");
        }

        return "";
    }

    public static String getJavaHomeInServer(boolean isDeployTool) {
        String javaDir = isWindows() ? "amd64-win" : "x86_64-linux";
        String javaHome = FileServiceImp.combinePath(Utils.getBasePath(), EnvironmentUtil.getServerRuntimePathName(), "runtime/java", javaDir, "bin");
        return javaHome;
    }

    // 对于这个方法在产品阶段用于判断是否具有JAVA_HOME
    public static boolean existJavaHome() throws Exception {
        ArrayList<String> inputs = getInputs("cmd /c echo %JAVA_HOME%");
        if (Utils.isNullOrEmpty(inputs)) {
            return false;
        }
        boolean existFlag = inputs.stream().anyMatch(input -> input.contains("%JAVA_HOME%"));
        return existFlag;
    }
}
